#include "commbase.h"
#include "log/logger.h"
#include "os/mutexlocker.h"
#include "lib/stringex.h"

CommBase::CommBase()
{
}

CommBase::~CommBase()
{
}

void CommBase::SetRespReceivedCallback(const RespReceivedCallback &callback)
{
    m_respCallback = callback;
}

void CommBase::Start()
{
    m_thread.SetThreadName("comm");
    m_thread.Start([=](){Loop();});
}

void CommBase::Stop()
{
    m_thread.Stop();
}

void CommBase::Send(std::shared_ptr<IProtocolMessage> msg)
{
    SendImpl(msg, false, 0);
}

std::shared_ptr<IProtocolMessage> CommBase::SendWait(std::shared_ptr<IProtocolMessage> msg, int timeout)
{
    return SendImpl(msg, true, timeout);
}

void CommBase::RecvByte(int c)
{
    m_recvQ.Post((uint8_t)c);
}

void CommBase::ConfigBase(IProtocol *protocol)
{
    m_protocol = protocol;
}

void CommBase::OnRespReceived(std::shared_ptr<IProtocolMessage> msg)
{
}

std::shared_ptr<IProtocolMessage> CommBase::SendImpl(std::shared_ptr<IProtocolMessage> msg, bool wait, int timeout)
{
    if(m_protocol == nullptr)
        return nullptr;
    if(msg == nullptr)
        return nullptr;

    //write
    auto wbo = WriteStreamImpl(msg, wait, timeout);
    if(wbo == nullptr) //error或者none wait
        return nullptr;

    //wait
    bool ok = wbo->Wait(timeout);
    if(!ok)
    {
        LogDebug()<<"wait timeout:"<<msg->ToString();
    }
    
    //remove
    m_waitListMutex.Lock();
    m_waitList.remove(wbo);
    m_waitListMutex.UnLock();

    return wbo->GetRecvMessage();
}

std::shared_ptr<WaitObject> CommBase::WriteStreamImpl(std::shared_ptr<IProtocolMessage> msg, bool wait, int timeout)
{
    if(m_protocol == nullptr)
        return nullptr;
    if(msg == nullptr)
        return nullptr;

    MutexLocker lock(&m_sendMutex);
    
    int pktLen = m_protocol->Packet(msg);
    if(pktLen == 0)
    {
        return nullptr; //error
    }
    
    if(!wait)
    {
        OnWriteStream(m_protocol->GetSendBuffer(), pktLen);
        return nullptr;
    }
    else //wait
    {   
        auto wbo = std::shared_ptr<WaitObject>(new WaitObject(m_protocol, msg));
        
        //add
        m_waitListMutex.Lock();
        m_waitList.push_back(wbo);
        m_waitListMutex.UnLock();
        
        //send
        OnWriteStream(m_protocol->GetSendBuffer(), pktLen);

        return wbo;
    }
}

void CommBase::Loop()
{
    while(!m_thread.IsInterruptionRequested())
    {
        char b;
        bool ok = m_recvQ.Wait(b);

        if(!ok)
            continue;
        if(m_protocol == nullptr)
            continue;

        // LogDebug().AsPure()<<StringEx::Format("%02X", b);
        auto msg = m_protocol->Parse(b);
        if(msg != nullptr)
        {   
            // LogDebug()<<"recv msg," << msg->ToString();
            HandleMessage(msg);
        }
    }
}

void CommBase::HandleMessage(std::shared_ptr<IProtocolMessage> msg)
{
    if(msg == nullptr)
        return;
    
    m_waitListMutex.Lock();
    for(auto it = m_waitList.begin(); it != m_waitList.end(); it++)
    {
        if((*it)->TryNotify(msg))
        {
            // m_waitList.erase(it); //send函数remove
            break;
        }
    }
    m_waitListMutex.UnLock();

    if(m_respCallback)
    {
        m_respCallback(msg);
    }

    OnRespReceived(msg);
}
